#pragma once
#include "log.hpp"
#include "socket.hpp"
#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

using namespace std;
class Server
{
    static const int size = sizeof(fd_set) * 8;
    static const int defaultfd = -1;

public:
    Server(const uint16_t &port) : _port(port), _listenfd(-1)
    {
    }
    ~Server()
    {
        close(_listenfd);
        delete[] _fdarray;
    }
    void Accepter(fd_set &rfds)
    {
        string ip;
        uint16_t port;
        int fd = Sock::Accept(_listenfd, ip, port);
        for (int i = 0; i < size; ++i)
        {
            if (_fdarray[i] == defaultfd)
            {
                _fdarray[i] = fd;
                break;
            }
        }
    }
    void Recver(int fd, int idx)
    {
        char buffer[1024];
        ssize_t s = recv(fd, buffer, sizeof(buffer) - 1, 0);
        if (s > 0)
        {
            buffer[s] = 0;
            // cout << buffer << endl;
            string ret(buffer);
            ssize_t s = send(fd, ret.c_str(), ret.size(), 0);
        }
        else if (s == 0)
        {
            logMessage(NORMAL, "client quit");
            _fdarray[idx] = defaultfd;
            close(fd);
        }
        else
        {
            logMessage(ERROR, "recv fail");
            _fdarray[idx] = defaultfd;
            close(fd);
        }
    }
    void handleEvent(fd_set &rfds)
    {
        for (int i = 0; i < size; ++i)
        {
            if (_fdarray[i] == defaultfd)
                continue;
            if (FD_ISSET(_fdarray[i], &rfds))
            {
                if (_fdarray[i] == _listenfd)
                {
                    Accepter(rfds);
                }
                else
                {
                    Recver(_fdarray[i], i);
                }
            }
        }
    }
    void init()
    {
        _listenfd = Sock::Socket();
        Sock::Bind(_listenfd, _port);
        Sock::Listen(_listenfd);
        _fdarray = new int[size];
        for (int i = 0; i < size; ++i)
            _fdarray[i] = defaultfd;
        _fdarray[0] = _listenfd;
    }
    void start()
    {
        while (true)
        {
            int maxfd = 0;
            fd_set rfds;
            for (int i = 0; i < size; ++i)
            {
                if (_fdarray[i] == defaultfd)
                    continue;
                FD_SET(_fdarray[i], &rfds);
                if (maxfd < _fdarray[i])
                    maxfd = _fdarray[i];
            }
            // timeval time = {3, 0};
            // int n = select(maxfd + 1, &rfds, nullptr, nullptr, &time);
            int n = select(maxfd + 1, &rfds, nullptr, nullptr, nullptr);

            switch (n)
            {
            case 0:
                logMessage(NORMAL, "timeout...");
                break;
            case -1:
                logMessage(WARNING, "select fail");
                break;
            default:
                logMessage(NORMAL, "get an event");
                handleEvent(rfds);
                break;
            }
        }
    }

private:
    uint16_t _port;
    int _listenfd;
    int *_fdarray;
};
